package java5.io;

public class Loserchess implements Constantes {

    private boolean noEsJugadaValida;
    private boolean esAgresivo;
    private int color;
    private int rival;
    private int oo;
    private int ooo;
    private int _oo;
    private int _ooo;
    private int _limiteagresivo;
    private int _limitedefensivo;
    private int[] opc;
    private int[] opc_rival;
    private int[][][] variantesjugadastrue;
    private int[][][] variantesjugadasfalse;
    private int[][][] cachememory;
    private UtilLoser util;
    private CalculoLoser calculo;
    private RecursivoLoser recursivo;

    public Loserchess(int jugador) {
        this.color = jugador;
        if (jugador == white) {
            this.oo = 0;
            this.ooo = 1;
            this._oo = 2;
            this._ooo = 3;
            this.rival = black;
        } else {
            this.oo = 2;
            this.ooo = 3;
            this._oo = 0;
            this._ooo = 1;
            this.rival = white;
        }
        util = new UtilLoser();
        calculo = new CalculoLoser(util);
        recursivo = new RecursivoLoser(util, calculo);
        opc = util.obtenerValoresPiezaXColor(color);
        opc_rival = util.obtenerValoresPiezaXColor(rival);
        cachememory = new int[5][31][4];
        variantesjugadastrue = new int[50][5000][64];
        variantesjugadasfalse = new int[50][5000][64];
    }

    public int[] jugar(int[] tablero, int jugada_ini, int jugada_fin, int jugada_prom, boolean[] enroques) {

        int[][] jugadas;
        int[] strJug = null;
        int i = 0, cnt;
        long l1;

        //mientras sea falso
        if (!esAgresivo) {
            esAgresivo = util.valorarTableroCompleto(tablero);
        } else {//verificamos si es el final
            if (util.valorarTableroAgresivo(tablero, color) == 1) {
                return null;
            }
        }

        //verificar el cachememory
        if (cachememory != null) {
            int[][] temp;
            int[] t;
            for (int k = 1; k < cachememory[0][0][0]; k++) {
                temp = cachememory[k];
                if (temp[0][0] > 2) {
                    t = temp[temp[0][1]];

                    if (t[1] == jugada_ini && t[2] == jugada_fin && (t[3] == 0 || t[3] == jugada_prom)) {
                        strJug = temp[temp[0][1] + 1];
                        temp[0][1] += 2;
                        if (cachememory.length > 2) {
                            cachememory[0][0][0] = 2;
                            cachememory[1] = temp;
                        }
                        System.out.println("**en cache: " + util.imprimirJugada(strJug));
                        return strJug;
                    }
                }
            }
        }

        //obtener jugadas posibles
        jugadas = calculo.devolverJugadas(
                tablero, color, rival, jugada_ini, jugada_fin, enroques[oo], enroques[ooo], false, opc, opc_rival);
        cnt = jugadas[0][0];

        if (cnt > 2) {
            jugadas = util.ordenarMisJugadas(jugadas, tablero);

            noEsJugadaValida = true;
            if (esAgresivo) {//buscamos jugada ganadora
                for (int[] j : jugadas) {
                    l1 = System.currentTimeMillis();

                    System.out.println("**xxAnalizando " + util.imprimirJugada(j) + "...");
                    _limiteagresivo = 0;

                    variantesjugadastrue[0][0][0] = 1;
                    variantesjugadasfalse[0][0][0] = 1;
                    noEsJugadaValida = juegoAgresivo(tablero, j, enroques);

                    System.out.println(noEsJugadaValida + "-_lim: " + _limiteagresivo);
                    System.out.println(System.currentTimeMillis() - l1);

                    if (!noEsJugadaValida && _limiteagresivo < topAgresivo) {
                        System.out.println("cache : " + util.mostrarCache(cachememory));
                        strJug = j;
                        break;
                    }
                }
            }

            if (noEsJugadaValida) {
                for (int[] j : jugadas) {
                    if (i < cnt - 0) {
                        l1 = System.currentTimeMillis();

                        _limitedefensivo = 0;
                        System.out.println("**Analizando " + util.imprimirJugada(j) + "...");

                        variantesjugadastrue[0][0][0] = 1;
                        variantesjugadasfalse[0][0][0] = 1;
                        noEsJugadaValida = juegoDefensivo(tablero, j, enroques);

                        util.mostrarLinea();
                        System.out.println("_lim: " + _limitedefensivo + "-" + noEsJugadaValida);
                        System.out.println(System.currentTimeMillis() - l1);

                        if (!noEsJugadaValida) {
                            strJug = j;
                            break;
                        }
                    } else {
                        System.out.println("Sin analizar " + util.imprimirJugada(j));
                        strJug = j;
                    }
                    i++;
                }
            }

        } else if (cnt == 1) {
            System.out.println("** no existen jugadas disponibles");
        } else {
            //devolver la única jugada disponible
            strJug = jugadas[1];
        }

        return strJug;
    }

    public boolean juegoDefensivo(int[] tablero, int[] jugada, boolean[] enroques) {

        int[][] jugadasposiblesRival;
        int[][] _jugadasposibles;
        boolean[] _enroques;
        int val;

        tablero = util.ingresarJugada(tablero, jugada, true);
        val = util.valorarTableroDefensivo(tablero, rival);

        // si después de mi jugada solo queda el rey rival entonces pruebo con otra jugada
        if (val == 1) {
            return true;
        } else {
            //buscar las jugadas del rival de acuerdo a la jugada elegida al azar
            jugadasposiblesRival = calculo.devolverJugadas(tablero, rival, color,
                    jugada[1], jugada[2], enroques[_oo], enroques[_ooo], true, opc_rival, opc);

            if (jugadasposiblesRival[0][0] == 0) {
                return true;
            } else {
                //recorro todas las jugadas posibles hasta encontrar una que invalide la
                //jugada elegida al azar en el método previo
                int size = jugadasposiblesRival[0][0];
                int[] _tablero;
                int[] jugada_rival;

                for (int i = 1; i < size; i++) {
                    jugada_rival = jugadasposiblesRival[i];
                    util.poblarLinea(0, jugada_rival);
                    _tablero = util.ingresarJugada(tablero, jugada_rival, size == 1 ? false : true);

                    //si después de la jugada del rival solo queda mi rey, pruebo con su otra jugada
                    // para validar que con cualquier jugada del rival siempre gano
                    _enroques = util.verificarEnroques(jugada_rival[0], jugada_rival[1], enroques, color == white, size == 1 ? false : true);

                    //obtengo mis jugadas después de la respuesta del rival
                    _jugadasposibles = recursivo.devolverJugadas(_tablero, color, rival, jugada_rival, opc, opc_rival);

                    if (_jugadasposibles[_cabeceraJugadas][_tieneJugadasObligatorias] == 1
                            || _jugadasposibles[_cabeceraJugadas][_hayJaque] == 1) {
                        if (this.juegoDefensivoRecursivo(_jugadasposibles, _tablero, _enroques, 1)) {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }

    private boolean juegoDefensivoRecursivo(int[][] jugadasposibles, int[] tablero,
            boolean[] enroques, int posi) {
        if (_limitedefensivo++ > topDefensivo) {
            return true;
        }

        int[][] _jugadasposibles;
        int[][] jugadasposiblesRival;
        int[] _tablero;
        boolean[] _enroques;
        boolean eslineavalida = false;
        int[] t;
        int val;
        int num_jugadas = jugadasposibles[0][0];
        int[] jugada;
        int idx = 0;
        int[][] vartrue = null;
        int[][] varfalse = null;
        int cntrue = 0;
        int cnfalse = 0;

        if (posi > 3) {
            idx = posi - (posi / 2 + 2);
            vartrue = variantesjugadastrue[idx];
            varfalse = variantesjugadasfalse[idx];
            cntrue = vartrue[0][0]== 0 ? 1 : vartrue[0][0];
            cnfalse = varfalse[0][0] == 0 ? 1 : varfalse[0][0];
        }

        mis_jugadas:
        for (int i = 1; i < num_jugadas; i++) {
            jugada = jugadasposibles[i];
            eslineavalida = false;

            util.poblarLinea(posi, jugada);
            t = util.ingresarJugada(tablero, jugada, num_jugadas == 1 ? false : true);
            val = util.valorarTableroDefensivo(t, rival);

            //si solo le queda el rey, o el rey y ningún peón se invalida la jugada
            if (val == 1 || val == -1) {
                util.findeLinea(posi, TRUE);
                return true;
            } else {
                if (posi > 3) {
                    int k;

                    for (k = 1; k < vartrue[0][0]; k++) {
                        if (util.compararTableros(t, vartrue[k])) {
                            util.findeLinea(posi, XTRUE);
                            eslineavalida = true;
                            continue mis_jugadas;
                        }
                    }

                    for (k = 1; k < varfalse[0][0]; k++) {
                        if (util.compararTableros(t, varfalse[k])) {
                            util.findeLinea(posi, XFALSE);
                            break mis_jugadas;
                        }
                    }
                }

                jugadasposiblesRival = calculo.devolverJugadas(t, rival, color,
                        jugada[1], jugada[2], enroques[_oo], enroques[_ooo], true, opc_rival, opc);

                //si no hay jugadas es porque se dio mate al rival por eso se descarta la jugada
                if (jugadasposiblesRival[0][0] == 1) {//solo cabecera
                    util.findeLinea(posi, TRUE);
                    eslineavalida = true;
                    continue mis_jugadas;
                } else {
                    //recorro todas las jugadas posibles hasta encontrar una que invalide mi jugada elegida
                    int size = jugadasposiblesRival[0][0];
                    boolean esCopia = size == 1 ? false : true;
                    int[] jugada_rival;

                    jugadas_del_rival:
                    for (int j = 1; j < size; j++) {
                        jugada_rival = jugadasposiblesRival[j];
                        util.poblarLinea(posi + 1, jugada_rival);
                        _tablero = util.ingresarJugada(t, jugada_rival, esCopia);
                        _enroques = util.verificarEnroques(jugada_rival[0], jugada_rival[1], enroques, color == white, esCopia);

                        _jugadasposibles = recursivo.devolverJugadas(_tablero, color, rival, jugada_rival, opc, opc_rival);

                        if (_jugadasposibles[_cabeceraJugadas][_tieneJugadasObligatorias] == 1) {
                            // si al rival solo le quedan 2 piezas y yo tengo jugada obligatoria, el final es obvio
                            if (val == 2) {
                                util.findeLinea(posi + 1, TRUE);
                                eslineavalida = true;
                                break jugadas_del_rival;
                            } else {
                                if (eslineavalida = this.juegoDefensivoRecursivo(_jugadasposibles, _tablero, _enroques, posi + 2)) {
                                    break jugadas_del_rival;
                                }
                            }
                        } else if (_jugadasposibles[_cabeceraJugadas][_hayJaque] == 1
                                && jugadasposibles[_cabeceraJugadas][_hayJaqueDoble] == 0) {
                            if (jugadasposibles[_cabeceraJugadas][_hayJaque] == 1) {
                                _jugadasposibles[_cabeceraJugadas][_hayJaqueDoble] = 1;
                            }
                            if (eslineavalida = this.juegoDefensivoRecursivo(_jugadasposibles, _tablero, _enroques, posi + 2)) {
                                break jugadas_del_rival;
                            }
                        } else {
                            util.findeLinea(posi + 1, FALSE);
                        }
                    }//ffor_rival
                    if (_limitedefensivo > topDefensivo) {
                        break mis_jugadas;
                    }
                }
            }
            if (!eslineavalida) {
                util.findeLinea(posi + 1, FALSE);
                if (posi > 3) {
                    varfalse[cnfalse++] = t;
                    this.guardarDatos(idx, cntrue, cnfalse);
                }
                break mis_jugadas;
            } else {
                if (posi > 3) {
                    vartrue[cntrue++] = t;
                    if (i == num_jugadas) {
                        this.guardarDatos(idx, cntrue, cnfalse);
                    }
                }
            }
        }//ffor

        return eslineavalida;
    }

    public boolean juegoAgresivo(int[] tablero, int[] jugada, boolean[] enroques) {

        int[][] _jugadasposiblesRival;
        boolean[] _enroques;
        int val;

        tablero = util.ingresarJugada(tablero, jugada, true);
        val = util.valorarTableroAgresivo(tablero, rival);

        // si después de mi jugada solo queda el rey rival entonces pruebo con otra jugada
        if (val == 1) {
            return true;
        } else {
            _enroques = util.verificarEnroques(jugada[0], jugada[1], enroques, color == white, true);
            _jugadasposiblesRival = recursivo.devolverJugadas(tablero, rival, color, jugada, opc_rival, opc);

            if (_jugadasposiblesRival[_cabeceraJugadas][_tieneJugadasObligatorias] == 1
                    || _jugadasposiblesRival[_cabeceraJugadas][_hayJaque] == 1) {

                if (!this.juegoAgresivoRecursivo(_jugadasposiblesRival, tablero, _enroques, 0)) {
                    cachememory[0][0][0] = _jugadasposiblesRival[_cabeceraJugadas][_cantidadJugadas];
                    return false;
                }
                cachememory[0][0][0] = 1;
            }
        }

        return true;
    }

    private boolean juegoAgresivoRecursivo(int[][] jugadasposiblesrival, int[] tablero,
            boolean[] enroques, int posi) {
        if (_limiteagresivo++ > topAgresivo) {
            return false;
        }
        int[][] _jugadasposiblesrival;
        int[][] jugadasposibles;
        int[] _tablero;
        boolean[] _enroques;
        boolean eslineavalida = true;
        int num_jugadas;
        int[] t;
        int[] jugada_rival;
        int val;
        int cnt = 0;
        int idx = posi - (posi / 2 + 1);
        int[][] vartrue = variantesjugadastrue[idx];
        int[][] varfalse = variantesjugadasfalse[idx];
        int cntrue = vartrue[0][0];
        int cnfalse = varfalse[0][0];

        num_jugadas = jugadasposiblesrival[0][0];

        jugadas_rival:
        for (int i = 1; i < num_jugadas; i++) {
            jugada_rival = jugadasposiblesrival[i];
            eslineavalida = true;

            util.poblarLinea(posi, jugada_rival);
            t = util.ingresarJugada(tablero, jugada_rival, num_jugadas == 1 ? false : true);
            val = util.valorarTableroAgresivo(t, color);

            if (val == 1) {
                util.findeLinea(posi, FALSE);
                return false;
            } else {
                if (posi > 2) {
                    int k;
                    for (k = 1; k < vartrue[0][0]; k++) {
                        if (util.compararTableros(t, vartrue[k])) {
                            eslineavalida = true;
                            util.findeLinea(posi, XTRUE);
                            break jugadas_rival;
                        }
                    }
                    for (k = 1; k < varfalse[0][0]; k++) {
                        if (util.compararTableros(t, varfalse[k])) {
                            eslineavalida = false;
                            util.findeLinea(posi, XFALSE);
                            continue jugadas_rival;
                        }
                    }
                }

                jugadasposibles = calculo.devolverJugadas(t, color, rival,
                        jugada_rival[1], jugada_rival[2], enroques[oo], enroques[ooo], true, opc, opc_rival);

                if (jugadasposibles[0][0] == 1) {//solo cabecera
                    eslineavalida = false;
                    util.findeLinea(posi, FALSE);
                    continue jugadas_rival;
                } else {
                    int size = jugadasposibles[0][0];
                    boolean esCopia = size == 1 ? false : true;
                    int[] jugada;

                    mis_jugadas:
                    for (int j = 1; j < size; j++) {
                        //for (int[] jugada : jugadasposibles) {
                        jugada = jugadasposibles[j];
                        util.poblarLinea(posi + 1, jugada);
                        _tablero = util.ingresarJugada(t, jugada, esCopia);
                        _enroques = util.verificarEnroques(jugada[0], jugada[1], enroques, color == white, esCopia);

                        _jugadasposiblesrival = recursivo.devolverJugadas(_tablero, rival, color, jugada, opc_rival, opc);

                        if (_jugadasposiblesrival[_cabeceraJugadas][_tieneJugadasObligatorias] == 1) {
                            // si al rival solo le quedan 2 piezas y yo tengo jugada obligatoria, el final es obvio
                            if (val == 2) {
                                eslineavalida = false;
                                util.findeLinea(posi + 1, FALSE);
                                break mis_jugadas;
                            } else {
                                if (!(eslineavalida = this.juegoAgresivoRecursivo(_jugadasposiblesrival, _tablero, _enroques, posi + 2))) {
                                    break mis_jugadas;
                                }
                            }
                        } else if (_jugadasposiblesrival[_cabeceraJugadas][_hayJaque] == 1
                                && jugadasposiblesrival[_cabeceraJugadas][_hayJaqueDoble] == 0) {
                            if (jugadasposiblesrival[_cabeceraJugadas][_hayJaque] == 1) {
                                _jugadasposiblesrival[_cabeceraJugadas][_hayJaqueDoble] = 1;
                            }
                            if (!(eslineavalida = this.juegoAgresivoRecursivo(_jugadasposiblesrival, _tablero, _enroques, posi + 2))) {
                                break mis_jugadas;
                            }
                        } else {
                            util.findeLinea(posi + 1, TRUE);
                        }
                    }//ffor_rival
                    if (_limiteagresivo > topAgresivo) {
                        break jugadas_rival;
                    }
                }
            }
            if (eslineavalida) {
                util.findeLinea(posi + 1, TRUE);
                if (posi > 2) {
                    vartrue[cntrue++] = t;
                    this.guardarDatos(idx, cntrue, cnfalse);
                }
                break jugadas_rival;
            } else {
                if (posi > 2) {
                    varfalse[cnfalse++] = t;
                    if (i == num_jugadas) {
                        this.guardarDatos(idx, cntrue, cnfalse);
                    }
                }
            }
            if (posi == 0) {
                util.poblarcache(cachememory[cnt + 1]);
            }
            cnt++;
        }//ffor

        return eslineavalida;
    }

    private void guardarDatos(int posi, int cntrue, int cnfalse) {
        //actualizamos las cantidades
        variantesjugadastrue[posi][0][0] = cntrue;
        variantesjugadasfalse[posi][0][0] = cnfalse;

        //eliminamos los superiores
        if (posi >= 3) {
            int key = posi + 1;
            while (true) {
                if (variantesjugadastrue[key][0][0] > 1) {
                    variantesjugadastrue[key][0][0] = 1;
                    variantesjugadastrue[0][0][0]--;
                    key++;
                } else {
                    break;
                }
            }

            key = posi + 1;

            while (true) {
                if (variantesjugadasfalse[key][0][0] > 1) {
                    variantesjugadasfalse[key][0][0] = 1;
                    variantesjugadasfalse[0][0][0]--;
                    key++;
                } else {
                    break;
                }
            }
        }
    }
}
